﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
using DigitalRune.Windows.Docking;
using md.imi.membrane.common;
using md.imi.membrane.common.Interfaces;
using md.imi.membranes.gui.Dialogs;
using md.imi.membranes.gui.elements;
using md.imi.membranes.objects;
using md.imi.membranes.objects.Gui;
using md.imi.membranes.objects.membranes;
using md.imi.membranes.objects.psystems;

namespace md.imi.membranes.gui.Editor
{
    /// <summary>
    ///     Current configuration editor form.
    /// </summary>
    public partial class CurrentConfiguration : DockableForm, IHandleMembraneSelection
    {
        private List<Solution> selectedSolutions = null;
        // private string lastProjectFileName = string.Empty;

        
        
        public event MembraneG.MmebraneSelectionHandler NotifyMembraneSelection;

        /// <summary>
        ///     Active project, which is currently displayed on 
        /// editor form.
        /// </summary>
        public PSystemsTree Project { get; set; }

        /// <summary>
        ///
        /// </summary>
        public CurrentConfiguration()
        {
            InitializeComponent();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="project"></param>
        public CurrentConfiguration(PSystemsTree project)
            : this()
        {
            Project = project;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GraphicalEditorForm_Activated(object sender, System.EventArgs e)
        {
            var parentForm = MdiParent as MainForm;
            if (parentForm != null) parentForm.SelectActiveProjectByEditorForm(this);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GraphicalEditorForm_Load(object sender, System.EventArgs e)
        {
            // draw membranes
            if (Project == null) 
            {
                return;
            }
           
            DrawMembranes(this, 1, 0, Project.Membranes, this, Color.LightGray);
        }

        /// <summary>
        ///     The method takes as an argument the new configuration and redraws it on the form's canvas.
        /// The new project is kept internally.
        /// </summary>
        /// <param name="configuration"> project to reload</param>
        /// <returns>true if project has been successfully reloaded</returns>
        public bool ReloadConfiguration(PSystemsTree configuration)
        {
            // discard old configuration if exists
            foreach (var ctrl in Controls)
            {
                if (ctrl is MembraneG)
                {
                    (ctrl as MembraneG).Parent = null;
                    (ctrl as MembraneG).Dispose();
                }
            }

            Project = configuration;
            if (configuration != null)
            {
                DrawMembranes(this, 1, 0, Project.Membranes, this, Color.LightGray);
                this.Invalidate();
                this.Update();
                return true;
            }

            // project was not reloaded
            return false;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GraphicalEditorForm_Shown(object sender, System.EventArgs e)
        {
            
        }

        /// <summary>
        ///     Set tab text
        /// </summary>
        public void UpdateProjectName(String name)
        {
            WorkSpace.Instance.SelectedProject.ProjectName = name;
            TabText = String.IsNullOrEmpty(name) ? "config-0" : name;
        }
        
        /// <summary>
        ///
        /// </summary>
        /// <param name="senderObj"></param>
        /// <param name="membraneObj"></param>
        public void MembraneSelectionEventHandler(object senderObj, object membraneObj)
        {
            var sender = senderObj as MembraneG;
            var memebrane = membraneObj as Membrane;

            if (sender == null || memebrane == null)
            {
                return;
            }

            // notify that a membrane was selected to subscribers
            if (NotifyMembraneSelection != null)
            {
                NotifyMembraneSelection(sender, memebrane);
            }

            foreach (var ctrl in Controls)
            {
                if (ctrl is MembraneG)
                {
                    var m = ctrl as MembraneG;

                    if (m == sender)
                    {
                        m.IsSelected = true;
                        m.DeselectAllSubmemranes();
                        m.BringToFront();
                    }
                    else
                        m.IsSelected = false;

                    m.Invalidate();
                }
            }
        }

        /// <summary>
        ///     On click on editor area, deselect all membranes defined 
        /// in this editor.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GraphicalEditorFormClick(object sender, System.EventArgs e)
        {
            foreach (var ctrl in Controls)
            {
                if (ctrl is MembraneG)
                {
                    (ctrl as MembraneG).IsSelected = false;
                    (ctrl as MembraneG).DeselectAllSubmemranes();
                    (ctrl as MembraneG).Invalidate();
                }
            }
        }

        #region Keyboard events handler

        /// <summary>
        ///
        /// </summary>
        /// <param name="keys"></param>
        internal void SendKeyPressToMembranes(Keys keys)
        {
            foreach (var ctrl in Controls)
            {
                if ((ctrl is MembraneG))
                {
                    (ctrl as MembraneG).HandleKeyPress(keys);
                }
            }
        }

        #endregion Keyboard events handler

        #region Drawing

        /// <summary>
        ///
        /// </summary>
        /// <param name="e"></param>

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            foreach (Control control in Controls)
            {
                control.Update();
                
            }
            /*
            var pn = new Pen(Color.LightCyan);

            for (int i = 5; i < (Width - 5); i += 15)
            {
                e.Graphics.DrawLine(pn, i, 5, i, Height - 5);
            }

            for (int j = 5; j < (Height - 5); j += 15)
            {
                e.Graphics.DrawLine(pn, 5, j, Width - 5, j);
            }
             */ 
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="count"></param>
        /// <param name="index"></param>
        /// <param name="membranes"></param>
        private void DrawMembranes(Control parent, int count, int index, MembranesTree membranes, IHandleMembraneSelection mainControl, Color color)
        {
            if (membranes.GuiInfo == null ||  membranes.GuiInfo.Size.IsEmpty)
            {
                var membrSize = new Size((parent.Size.Width / count) - 10, (parent.Size.Height / count) - 10);

                var membrLocation = new Point(
                    parent.Location.X + ((membrSize.Width + 5) * index),
                    parent.Location.Y + ((membrSize.Height + 5) * index)
                   );
                membranes.GuiInfo = new GuiInfo(membrLocation, membrSize);
            }
            
            

            var newControl = new MembraneG(membranes);
            membranes.GuiInfo.Control = newControl;
            newControl.MembraneParnthesisColor = color;

            newControl.EventMembraneSelection += ((IHandleMembraneSelection)parent).MembraneSelectionEventHandler;
            newControl.EventMembraneSelection += mainControl.MembraneSelectionEventHandler;
            newControl.HighlightSolutions(selectedSolutions);

            parent.Controls.Add(newControl);
            newControl.Visible = true;


            newControl.Location = membranes.GuiInfo.Location;
            
            newControl.Size = membranes.GuiInfo.Size;

            if (membranes.Membranes == null)
                return;

            for (int i = 0; i < membranes.Membranes.Count; i++)
            {
                DrawMembranes((Control)membranes.GuiInfo.Control,
                              membranes.Membranes.Count, i, membranes.Membranes[i], mainControl, Color.Red);  // recursively draw all membrane
            }
        }

        #endregion Drawing

        #region File Operations



        private void SaveProjectToFile(String fileName)
        {
            try
            {
                WorkSpace.Instance.ExportToXml(fileName);
                FrmMessageBox.ShowInfo("Project Saved to : " + fileName);
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex);
                FrmMessageBox.ShowError("Failed to serialize to XML", ex);
            }
  
        }

        /// <summary>
        ///     Export to XML
        /// </summary>
        public void SaveProject()
        {
            if (Project == null)
            {
                FrmMessageBox.ShowError("Project is not defined to be saved!");
                return;
            }

            if (String.IsNullOrEmpty(WorkSpace.Instance.SelectedProject.ProjectFileName))
            {
                var saveDialog = new SaveFileDialog();

                saveDialog.Title    = "Save pSystem to ";
                saveDialog.Filter   = "ProjectFiles (*.pxml)|*.pxml|All files (*.*)|*.* ";

                // save current pSystem to a file
                if (saveDialog.ShowDialog() != DialogResult.OK)
                {
                    return;
                }

                UpdateProjectName(saveDialog.FileName);
            }

            SaveProjectToFile(WorkSpace.Instance.SelectedProject.ProjectFileName);

        }

        /// <summary>
        ///     Always ask for a file name
        /// </summary>
        internal void SaveProjectAs()
        {
            if (Project == null)
            {
                FrmMessageBox.ShowError("Project is not defined to be saved!");
                return;
            }

            var saveDialog = new SaveFileDialog
                                 {
                                     Title = "Save pSystem As ",
                                     Filter = "ProjectFiles (*.pxml)|*.pxml|All files (*.*)|*.* "
                                 };

            // save current pSystem to a file
            if (saveDialog.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            UpdateProjectName(saveDialog.FileName);
            SaveProjectToFile(saveDialog.FileName);
        }

      
        #endregion File Operations

        #region Test Project Files loading
       
        public void OpenTestProject(string path)
        {
            //------------------------------------------------------------------------------
            // conf 0
            PSystemsTree conf_0 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0.pxml"));
            
            WorkSpace.Instance.SelectedProject = conf_0;
            
            //------------------------------------------------------------------------------
            PSystemsTree conf_0_1 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_1, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_1.pxml"));
            conf_0.PSystems.Add(conf_0_1);

            // + 1
            PSystemsTree conf_0_1_1 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_1_1, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_1_1.pxml"));
            conf_0_1.PSystems.Add(conf_0_1_1);

            // +2
            PSystemsTree conf_0_1_1_1 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_1_1_1, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_1_1_1.pxml"));
            conf_0_1_1.PSystems.Add(conf_0_1_1_1);
            
            //------------------------------------------------------------------------------
            PSystemsTree conf_0_2 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_2, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_2.pxml"));
            WorkSpace.Instance.SelectedProject.PSystems.Add(conf_0_2);

            // +1 
            PSystemsTree conf_0_2_1 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_2_1, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_2_1.pxml"));
            conf_0_2.PSystems.Add(conf_0_2_1);

            PSystemsTree conf_0_2_2 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_2_2, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_2_2.pxml"));
            conf_0_2.PSystems.Add(conf_0_2_2);

            PSystemsTree conf_0_2_3 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_2_3, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_2_3.pxml"));
            conf_0_2.PSystems.Add(conf_0_2_3);

            PSystemsTree conf_0_2_4 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_2_4, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_2_4.pxml"));
            conf_0_2.PSystems.Add(conf_0_2_4);

            //------------------------------------------------------------------------------
            PSystemsTree conf_0_3 = null;
            WorkSpace.Instance.ImportFromXml(out conf_0_3, String.Format("{0}{1}{2}",
                path, Path.DirectorySeparatorChar, "conf_0_3.pxml"));
            WorkSpace.Instance.SelectedProject.PSystems.Add(conf_0_3);
        }
            
        #endregion
        
        #region Tool Box Click processing

        /// <summary>
        ///     Tool button constants
        /// </summary>
        private enum ToolButtonsTag
        {
            tagCreateNewMembrane,
            tagAddMultiset,
            tagEditMembrane
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="stringTag"></param>
        /// <param name="tag"></param>
        /// <returns></returns>
        private bool IsEqualToTag(String stringTag, ToolButtonsTag tag)
        {
            return stringTag.Equals(Enum.GetName(typeof(ToolButtonsTag), tag), StringComparison.CurrentCultureIgnoreCase);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToolBoxButtonClick(object sender, EventArgs e)
        {
            if (!(sender is ToolStripItem) || ((sender as ToolStripItem).Tag == null))
            {
                Logger.Instance.Info("tool bar button press not recognized");
                return;
            }

            var btnTag = (sender as ToolStripItem).Tag.ToString();

            if (IsEqualToTag(btnTag, ToolButtonsTag.tagCreateNewMembrane))
            {
                FrmMessageBox.ShowInfo("Create new membrane");
            }

            if (IsEqualToTag(btnTag, ToolButtonsTag.tagAddMultiset))
            {
                FrmMessageBox.ShowInfo("Create new multiset");
            }
        }

        #endregion Tool Box Click processing

        #region Membrane selection actions
        private bool EditSelectedMembane(MembranesTree membranes)
        {
            if (membranes != null)
            {
                return false;
            }

            /*
            foreach (var membrane in Project.Membranes)
            {
            }
             * */

            return false;
        }

        private void editToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (Project != null)
            {
                EditSelectedMembane(Project.Membranes);
            }
        }

        public void HiglightSelectedSolutions(List<Solution> solutions)
        {
            Console.WriteLine("------------------------ SELECTED --------------------");
            selectedSolutions = solutions;

            foreach (var ctrl in Controls)
            {
                if (ctrl is MembraneG)
                {
                    (ctrl as MembraneG).HighlightSolutions(solutions);
                }
            }

            Refresh();
        }
        #endregion
        
        #region Export Image to a File

        public void ExportToImage()
        {
            if (Project == null || Project.Membranes == null ||
                Project.Membranes.GuiInfo == null || Project.Membranes.GuiInfo.Control == null)
            {
                return;
            }

            var saveDialog = new SaveFileDialog();

            saveDialog.Title = "Export membranes to image ";
            saveDialog.Filter = "Image (*.png)|*.ong|All files (*.*)|*.* ";

            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                SaveControlShot((Control)Project.Membranes.GuiInfo.Control, saveDialog.FileName, ImageFormat.Png);
                // Project.Membranes
            }
        }

        /// <summary>
        /// Saves a screen capture of the specified control to a file.
        /// </summary>
        /// <param name="control"></param>
        /// <param name="filename"></param>
        /// <param name="format"></param>
        private void SaveControlShot(Control control, string filename, ImageFormat format)
        {
            Bitmap controlShot = CaptureControlShot(control);

            controlShot.Save(filename, format);
        }

        /// <summary>
        /// Gets a bitmap image of the specified control.
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        private Bitmap CaptureControlShot(Control control)
        {
            // get control bounding rectangle
            Rectangle bounds = control.Bounds;

            // create the new bitmap that holds the image of the control
            Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);

            // copy controls image data to the bitmaps graphics device
            using (Graphics gr = Graphics.FromImage(bitmap))
            {
                gr.CopyFromScreen(control.Location, Point.Empty, bounds.Size);
            }

            return bitmap;
        }

        #endregion Export Image to a File


    }
}